package com.humble.voiceline

import android.content.Context
import android.content.res.TypedArray
import android.graphics.*
import android.util.AttributeSet
import android.view.View
import com.humble.voiceline.R
import java.util.*

/**
 * 自定义声音振动曲线view
 */
class VoiceLineView : View {
    private val LINE = 0
    private val RECT = 1
    private var middleLineColor = Color.BLACK
    private var voiceLineColor = Color.BLACK
    private var middleLineHeight = 4f
    private var paint: Paint? = null
    private var paintVoiceLine: Paint? = null
    private var mode = 0   // 0 线形 1 矩形

    /**
     * 灵敏度
     */
    private var sensibility = 4
    private var maxVolume = 100f
    private var translateX = 0f
    private var isSet = false

    /**
     * 振幅
     */
    private var amplitude = 1f

    /**
     * 音量
     */
    private var volume = 10f
    private var fineness = 1
    private var targetVolume = 1f
    private var speedY: Long = 50
    private var rectWidth = 25f
    private var rectSpace = 5f
    private var rectInitHeight = 4f
    private var rectList: MutableList<Rect>? = null
    private var lastTime: Long = 0
    private var lineSpeed = 90
    var paths: MutableList<Path>? = null

    constructor(context: Context) : super(context) {
        initAttrs(context, null)
    }
    constructor(context: Context, attrs: AttributeSet?) : super(context, attrs) {
        initAttrs(context, attrs)
    }

    constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(
        context,
        attrs,
        defStyleAttr
    ) {
        initAttrs(context, attrs)
    }

    private fun initAttrs(context: Context, attrs: AttributeSet?) {
        val typedArray = context.obtainStyledAttributes(attrs, R.styleable.VoiceLineView)
        mode = typedArray.getInt(R.styleable.VoiceLineView_viewMode, 0)
        voiceLineColor = typedArray.getColor(R.styleable.VoiceLineView_voiceLine, Color.BLACK)
        maxVolume = typedArray.getFloat(R.styleable.VoiceLineView_maxVolume, 100f)
        sensibility = typedArray.getInt(R.styleable.VoiceLineView_sensibility, 4)
        if (mode == RECT) {
            rectWidth = typedArray.getDimension(R.styleable.VoiceLineView_rectWidth, 25f)
            rectSpace = typedArray.getDimension(R.styleable.VoiceLineView_rectSpace, 5f)
            rectInitHeight = typedArray.getDimension(R.styleable.VoiceLineView_rectInitHeight, 4f)
        } else {
            middleLineColor = typedArray.getColor(R.styleable.VoiceLineView_middleLine, Color.BLACK)
            middleLineHeight =
                typedArray.getDimension(R.styleable.VoiceLineView_middleLineHeight, 4f)
            lineSpeed = typedArray.getInt(R.styleable.VoiceLineView_lineSpeed, 90)
            fineness = typedArray.getInt(R.styleable.VoiceLineView_fineness, 1)
            paths = ArrayList(20)
            for (i in 0..19) {
                paths?.add(Path())
            }
        }
        typedArray.recycle()
    }

    override fun onDraw(canvas: Canvas) {
        if (mode == RECT) {
            drawVoiceRect(canvas)
        } else {
            drawMiddleLine(canvas)
            drawVoiceLine(canvas)
        }
        run()
    }

    private fun drawMiddleLine(canvas: Canvas) {
        if (paint == null) {
            paint = Paint()
            paint!!.color = middleLineColor
            paint!!.isAntiAlias = true
        }
        canvas.save()
        canvas.drawRect(
            0f,
            (height - middleLineHeight) / 2,
            width.toFloat(),
            (height + middleLineHeight) / 2,
            paint!!
        )
        canvas.restore()
    }

    private fun drawVoiceLine(canvas: Canvas) {
        lineChange()
        if (paintVoiceLine == null) {
            paintVoiceLine = Paint()
            paintVoiceLine!!.color = voiceLineColor
            paintVoiceLine!!.isAntiAlias = true
            paintVoiceLine!!.style = Paint.Style.STROKE
            paintVoiceLine!!.strokeWidth = 2f
        }
        canvas.save()
        val moveY = height / 2
        for (i in paths!!.indices) {
            paths!![i].reset()
            paths!![i].moveTo(width.toFloat(), height / 2.0f)
        }
        var i = (width - 1).toFloat()
        while (i >= 0) {
            amplitude = 4 * volume * i / width - 4 * volume * i * i / width / width
            for (n in 1..paths!!.size) {
                val sin = amplitude * Math.sin(
                    (i - Math.pow(
                        1.22,
                        n.toDouble()
                    )) * Math.PI / 180 - translateX
                ).toFloat()
                paths!![n - 1].lineTo(
                    i,
                    2 * n * sin / paths!!.size - 15 * sin / paths!!.size + moveY
                )
            }
            i -= fineness.toFloat()
        }
        for (n in paths!!.indices) {
            if (n == paths!!.size - 1) {
                paintVoiceLine!!.alpha = 255
            } else {
                paintVoiceLine!!.alpha = n * 130 / paths!!.size
            }
            if (paintVoiceLine!!.alpha > 0) {
                canvas.drawPath(paths!![n], paintVoiceLine!!)
            }
        }
        canvas.restore()
    }

    private fun drawVoiceRect(canvas: Canvas) {
        if (paintVoiceLine == null) {
            paintVoiceLine = Paint()
            paintVoiceLine!!.color = voiceLineColor
            paintVoiceLine!!.isAntiAlias = true
            paintVoiceLine!!.style = Paint.Style.STROKE
            paintVoiceLine!!.strokeWidth = 2f
        }
        if (rectList == null) {
            rectList = LinkedList()
        }
        val totalWidth = (rectSpace + rectWidth).toInt()
        if (speedY % totalWidth < 6) {
            val rect = Rect(
                (-rectWidth - 10 - speedY + speedY % totalWidth).toInt(),
                ((height  - rectInitHeight) / 2 - if (volume == 10f) 0f else volume / 2).toInt(),
                (-10 - speedY + speedY % totalWidth).toInt(),
                ((height + rectInitHeight) / 2 + if (volume == 10f) 0f else volume / 2).toInt()
            )

            if (rectList!!.size > width / (rectSpace + rectWidth) + 2) {
                rectList!!.removeAt(0)
            }
            rectList!!.add(rect)
        }
        canvas.translate(speedY.toFloat(), 0f)
        for (i in rectList!!.indices.reversed()) {
            canvas.drawRect(rectList!![i], paintVoiceLine!!)
        }
        rectChange()
    }

    fun setVolume(volume: Int) {
        if (volume > maxVolume * sensibility / 25) {
            isSet = true
            targetVolume = height * volume / 2 / maxVolume
        }
    }

    private fun lineChange() {
        if (lastTime == 0L) {
            lastTime = System.currentTimeMillis()
            translateX += 1.5f
        } else {
            if (System.currentTimeMillis() - lastTime > lineSpeed) {
                lastTime = System.currentTimeMillis()
                translateX += 1.5f
            } else {
                return
            }
        }
        if (volume < targetVolume && isSet) {
            volume += (height / 30).toFloat()
        } else {
            isSet = false
            if (volume <= 10) {
                volume = 10f
            } else {
                volume -= if (volume < height / 30) {
                    (height / 60).toFloat()
                } else {
                    (height / 30).toFloat()
                }
            }
        }
    }

    private fun rectChange() {
        speedY += 6
        if (volume < targetVolume && isSet) {
            volume += (height / 30).toFloat()
        } else {
            isSet = false
            if (volume <= 10) {
                volume = 10f
            } else {
                volume -= if (volume < height / 30) {
                    (height / 60).toFloat()
                } else {
                    (height / 30).toFloat()
                }
            }
        }
    }

    fun run() {
        if (mode == RECT) {
            postInvalidateDelayed(30)
        } else {
            invalidate()
        }
    }
}